"""
reading a time series
and count rain event.

what is  a storm?
If it is not dry then it is a storm.
then what is dry?
Any 24 hour period with less than 0.1 total inches of precipitation.

Clear?
Let's do it
"""

import datetime, pyodbc


def get_cursor(db, password):
    """
    A helper function to return a cursor to the MDB file
    """
    conn_str = 'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=%s' % (db, password)
    cnxn = pyodbc.connect(conn_str)
    return cnxn.cursor()


class StormCounter:
    """
    connect to rainfall time series table in a mdb file and extract the storm events
    
    db = os.path.join(root, 'data', 'rainfall.mdb')
    password = ''
    ts = {'db': db, 'password': password, 'table': 'rg24', 'fields': {'time': 'time', 'value': 'rainfall', 'weather': 'weather'}, 'timestep': datetime.timedelta(minutes=5)}
    rain_def = {'max_duration': datetime.timedelta(days=1), 'min_total': 0.1, 'timestep': datetime.timedelta(minutes=5)}
    storms = StormCounter(ts, rain_def).count()
    """

    def __init__(self, ts, rain_def):
        #ts: time series definition, database, table
        #rain_def: storm definition
        self.ts = ts
        self.rain_def = rain_def
        self.cursor = get_cursor(self.ts['db'], self.ts['password'])
        
    def get_timeseries(self):
        #get the cursor to the timeseries 
        table = self.ts['table']
        time_fld = self.ts['fields']['time']
        value_fld = self.ts['fields']['value']
        weather_fld = self.ts['fields']['weather']
        sql = 'select [%s], [%s] from %s where [%s] > 0 order by [%s]' % (time_fld, value_fld, table, time_fld, time_fld)
        return self.cursor.execute(sql)


    def count(self):
        """
        Algrithm:
        First identify dry periods by
            1. Filter only the non-zero points and order them by time.
            2. The dry period is the duration between two adjancent point
            3. If it is greater than the MAX Duration (eg. 1 day), then it is a dry period
            4. If a storm is less than 0.1in total, then it is a dry period.
        Second check if the existing storm is large enough
        Thrid, if it meets both creteria, it is a storm
        """

        storm = {}
        storms = []
        dry = {} #used to count the length of the dry period
        t0 = None #previous point

        table = self.ts['table']
        time_fld = self.ts['fields']['time']
        value_fld = self.ts['fields']['value']
        
        
        
        for x in self.get_timeseries():
            #current time step
            t = [getattr(x, time_fld), getattr(x, value_fld)]
            
            #the first reading
            if storm=={}:
                storm['total'] = 0
                storm['start'] = t[0]
                t0 = t #initial value of previous data point
            
            #ignore all the zero time steps
            
            if t[1]>0:
                #if the dry peiod is long enough, it is the end of a storm
                if t[0] - t0[0] > self.rain_def['max_duration']:
                    #A dry period found
                
                    storm['end'] = t0[0]
                    if storm['total'] >= self.rain_def['min_total']:
                        # a storm identified
                        storms.append(storm)
                    #start a new storm count
                    storm = {'start': t[0], 'total': t[1]}
                #during a storm, keep calculating the total
                else:
                    storm['total'] += t[1]
                #keep a previous point
                t0 = t
        #the last data point
        #if it is in the middle of a storm, it should be closed
        if storm:
            storm['end'] = t0[0]
            storm['total'] += t0[1]
            if storm['total'] >= self.rain_def['min_total']:
                storms.append(storm)
            
        return storms    
   



##processing the data
##data stored in a mdb file
#db = r'M:\proj\0921\6002\0004 Private Source I&I Investigations\WIBs\Rain Data\rainfall.mdb'
#password = ''
#ts = {'db': db, 'password': password, 'table': 'rg24', 'fields': {'time': 'time', 'value': 'rainfall', 'weather': 'weather'}, 'timestep': datetime.timedelta(minutes=5)}
#rain_def = {'max_duration': datetime.timedelta(days=1), 'min_total': 0.1, 'timestep': datetime.timedelta(minutes=5)}
#sc = StormCounter(ts, rain_def)
#
##count the storms
#z = sc.count()
#
##write the csv file
#import csv
#f = open(r'M:\proj\0921\6002\0004 Private Source I&I Investigations\WIBs\Rain Data\storms2.csv', 'w')
#writer = csv.writer(f, lineterminator='\n')
#writer.writerow(['from', 'to', 'total', 'duration'])
#
#for x in z:
#    writer.writerow((x['start'].strftime('#%m/%d/%Y %H:%M#'), x['end'].strftime('#%m/%d/%Y %H:%M#'), x['total'], (x['end'] - x['start']).days*24 + (x['end'] - x['start']).seconds/60.0/60.0))
#
#f.close()
#
##write to a calendar file
#from icalendar  import Calendar, Event
#
#c = Calendar()
#for x in z:
#    duration = (x['end'] - x['start']).days*24 + (x['end'] - x['start']).seconds/60.0/60.0
#
#    desc = '%.1fhr(%.1fin) - rg24\n' % (duration, x['total']) + '%s - %s ' % (x['start'].strftime('#%m/%d/%Y %H:%M#'), x['end'].strftime('#%m/%d/%Y %H:%M#'))
#    
#    
#    e = Event()
#    e.add('summary', '%.1fhr(%.1fin)' % (duration, x['total']))
#    e.add ('dtstart', x['start'])
#    e.add('dtend', x['end'])
#    e.add('location', 'rg24')
#    e.add('description', desc)
#    c.add_component(e)
#
#    
#f = open(r'M:\proj\0921\6002\0004 Private Source I&I Investigations\WIBs\Rain Data\storms2.ics', 'wb')
#
#f.write(c.as_string())
#f.close()
